顺序队列Queue——数据结构系列教程(c++版)

梦想不会自己发光,真正闪耀的是那个为梦狂奔的你。献给知行的孩子们!(Eric.He著)


  队列是数据结构中经典的先进先出(FIFO, First In First Out)线性结构,就像日常生活中的排队买票——先排队的人先购票,后排队的人后购票。本教程基于数组实现的队列,从核心原理、代码结构到实战使用,全面讲解队列的原理与实现,功能包含入队、出队、获取队列头元素、获取队列尾元素等核心功能。

教程目录导航

一、队列的核心原理

1.1 队列的性质

  1. 先进先出(FIFO):这是队列的核心规则,新元素从队尾(tail)加入(入队),元素从队头(head)取出(出队),不存在中间插入或删除的操作。
  2. 线性结构:队列属于线性数据结构,元素之间存在一对一的前后逻辑关系,不存在分支或层级结构,符合线性表的基本特征(与数组、链表、栈一致)。
  3. 操作限制:队列的操作具有明确的限制性,仅支持两端的操作,不支持随机访问或中间位置的插入 / 删除
    • 只能在队尾进行 “入队” 操作(添加元素);
    • 只能在队头进行 “出队” 操作(删除元素);
    • 可以查看队头 / 队尾元素,但无法直接访问队列中间的元素。
  4. 队列状态:
    • 空队列:队列中不包含任何有效元素,此时无法执行出队操作(下溢);
    • 满队列(仅存在于固定容量队列):队列已达到最大存储容量,此时无法执行入队操作(上溢)。

演示动画

1.2 队列的分类

  1. 普通队列(Circular Queue):满足核心性质的队列
    • 如果存储结构采用数组实现普通队列,会存在空间浪费的问题:
    • 入队时tail向后移动,出队时head向后移动,随着操作的进行,head前面的数组空间会被闲置,即使数组后面还有空位,也无法利用(因为tail已经到达数组末尾)。
  2. 循环队列(Circular Queue):又称 “环形队列”,是对普通顺序队列的优化
    • 核心思想是让数组首尾相连,形成一个逻辑上的环形结构
    • 将数组首尾逻辑相连,当队尾指针到达数组末尾时,若队头前方有闲置空间,队尾指针重新指向数组起始位置;
    • 通过取模运算实现指针的循环移动,充分利用数组存储空间,解决普通顺序队列 “假溢出”(数组未满但无法入队)的问题;
    • 常见判空 / 判满方式:① 利用元素计数器(qty);② 预留一个数组空位,通过(tail+1)%MAXSIZE == head判满,head == tail判空。
  3. 双端队列(Deque, Double-Ended Queue):又称 “双向队列”,打破了普通队列的操作限制
    • 允许在队头和队尾两端同时执行入队和出队操作;
    • 可分为 “顺序双端队列”(数组实现)和 “链式双端队列”(链表实现);
    • 应用场景:滑动窗口、缓存管理等。
  4. 优先级队列(Priority Queue):不再遵循 “先进先出” 规则,而是根据元素的优先级决定出队顺序
    • 优先级高的元素优先出队,优先级相同的元素按入队顺序排列;
    • 底层通常基于堆(大根堆 / 小根堆)实现,也可基于有序数组、有序链表实现;
    • 应用场景:任务调度(高优先级任务先执行)、事件驱动模拟(按时间戳处理事件)、Top-K 问题等。
  5. 阻塞队列(Blocking Queue):属于 “线程安全队列”,是多线程编程中的常用数据结构
    • 当队列满时,入队操作会阻塞线程,直到队列有空闲空间;当队列为空时,出队操作会阻塞线程,直到队列中有元素;
  6. 并发队列(Concurrent Queue):专为多线程环境设计的队列,保证高并发场景下的线程安全和高效性;
    • 分为 “阻塞并发队列”(如阻塞队列)和 “无锁并发队列”(基于 CAS 算法实现,无需加锁,性能更高);

1.3 核心操作

操作名称 功能描述 时间复杂度
入队 将指定元素添加到队尾,队列满时抛出异常或返回失败标识 O(1)
出队 从队头移除并返回元素,队列为空时抛出异常或返回失败标识 O(1)
判空 判断队列是否为空,返回布尔值(true = 空,false = 非空) O(1)
判满 判断固定容量队列是否已满,返回布尔值(true = 满,false = 未满)(链式队列无需此操作) O(1)
获取队头元素 查看队头元素的值,不执行移除操作,队列为空时抛出异常或返回无效值 O(1)
获取队尾元素 查看队尾元素的值,不执行移除操作,队列为空时抛出异常或返回无效值 O(1)
获取队列大小 查看队列元素数量,队列为空时返回0 O(1)

1.4 存储结构

队列可以采用顺序存储和链式存储两种形式:

(1)顺序存储

(1)链式存储

每一个节点有二个域,即data数据域、next下一个节点地址。

二、代码结构解析

2.1 整体架构

提供的代码是模板化的队列实现(支持任意类型),采用C++结构体封装,分为头文件(声明)和源文件(实现)两部分,核心结构如下:

模块 作用 关键结构/函数
队列结构体 封装所有操作 ArrayQueue<T>(模板类)

2.2 关键结构体详解

(1)队列结构体(ArrayQueue)

template <typename T>
struct ArrayQueue{
    // ------------------------------------------------------------------------------------------------------
    //                                          私有成员
    // 注:私有成员只能在该结构体内部访问调用,外部通过该结构体定义(实例化)的变量(对象)不能访问调用)
    //------------------------------------------------------------------------------------------------------
    private:

        //---------------声明私有成员变量---------------

        T data[MAXSIZE];   // 存储数据,可存储字符或整数
        int head;        // 队列头,初始为-1表示空栈
        int tail;        // 队列尾,初始为-1表示空栈
        int qty;         // 用于跟踪队列中的元素数量

        //---------------声明私有成员函数---------------

    // ------------------------------------------------------------------------------------------------------
    //                                          公共成员
    // 注:公共成员能在该结构体内部访问调用,外部通过该结构体定义(实例化)的变量(对象)也能访问调用)
    //------------------------------------------------------------------------------------------------------
    public:

        //---------------声明公共成员函数---------------

        // 构造函数:外部通过该结构体定义(实例化)变量(对象)时,自动执行该函数(主要用于初始化成员变量的值)
        ArrayQueue() {
            head = 0; tail = 0; qty = 0;
            for(int i=0;i < MAXSIZE;i++) { data[i] = T(); }
        }

        int isEmpty();// 判断队列是否为空
        int isFull();// 判断队列是否已满
        void enQueue(const T& value);// 入队操作(在队列尾入队)
        T deQueue();// 出队操作(返回队列头元素,队列空返回-1)
        T getHead();// 获取队列头元素(不弹出,队列空返回-1)
        T getTail();// 获取队列尾元素(不弹出,队列空返回-1)
        int getSize();// 获取队列大小
        void print();// 打印队列数据
        void destroyQueue();// 销毁整个队列(防止内存泄漏)

        // 析构函数:自动销毁队列,避免内存泄漏
        ~ArrayQueue() {
        }
};

三、核心操作实现详解

3.1 获取元素

(1)获取队头元素(getHead)

template <typename T>
T ArrayQueue<T>::getHead()
{
    if (isEmpty()) {
        return T();
    }
    return data[head];
}

(2)获取队列尾元素(getTail)

template <typename T>
T ArrayQueue<T>::getTail()
{
    if (isEmpty()) {
        return T();
    }
    int index = (tail - 1 + MAXSIZE) % MAXSIZE;// 计算队尾元素的真实下标(适配循环队列)
    return data[index];
}

3.2 入队(enQueue)

template <typename T>
void ArrayQueue<T>::enQueue(const T& value)
{
    if (isFull())
    {
        printf("队列已满!无法入队。\n");
        return;
    }

    data[tail] = value;
    tail = (tail + 1) % MAXSIZE; //实现循环队列
    qty++;
}

3.3 出队(deQueue)

template <typename T>
T ArrayQueue<T>::deQueue()
{
    if(isEmpty())
    {
        printf("队列为空,无法出队。\n");
        return T();
    }

    T value =data[head];
    data[head] = T(); // 可选:清空出队的位置
    head = (head + 1) % MAXSIZE;
    qty--;
    return value;
}

四、实战使用示例

4.1 基础类型(int)使用示例

#include "ArrayQueue.h"
#include <iostream>

int main() {
    ArrayQueue<int> queues;

    // 1. 入队测试
    printf("\n=== 入队测试 ===\n");
    queues.enQueue(1);
    queues.enQueue(2);
    queues.enQueue(3);
    queues.enQueue(4);
    queues.enQueue(5);
    printf("入队1、2、3、4、5后:\n");
    queues.print();

    // 2. 出队测试
    printf("\n=== 出队测试 ===\n");
    printf("出队3次:");
    printf("%d ",queues.deQueue());
    printf("%d ",queues.deQueue());
    printf("%d \n",queues.deQueue());
    printf("出队后");
    queues.print();

    return 0;
}

4.2 输出结果


=== 入队测试 ===
入队1、2、3、4、5后:
队列内容:1 2 3 4 5
队列状态:头指针=0,尾指针=5,元素数量=5

=== 出队测试 ===
出队3次:1 2 3
出队后队列内容:4 5
队列状态:头指针=3,尾指针=5,元素数量=2
            

五、完整可运行代码

5.1 Entitys.h 头文件


#ifndef ENTITYS_H_INCLUDED
#define ENTITYS_H_INCLUDED

//************************************************************************************************************************************************************************
//                                                                      自定义类型
//************************************************************************************************************************************************************************

//========================================================================================================================================================================
//                                                                    学生结构体(Student)
//========================================================================================================================================================================
struct Student {
    int id;// 学号
    std::string name;// 姓名
    std::string dob;// 出生日期
    std::string sex;// 性别
    std::string gender;// 民族
    std::string address;// 地址
    float score;// 入学总分
    std::string school;// 学校
    std::string team;// 班级
    std::string status;// 状态

    bool operator<(const Student& other) const { return id < other.id; }
    bool operator>(const Student& other) const { return id > other.id; }
    bool operator==(const Student& other) const { return id == other.id; }
    bool operator!=(const Student& other) const { return id != other.id; }

    friend std::ostream& operator<<(std::ostream& os, const Student& s) {
        os << "[" << s.id<< ", " << s.name << ", " << s.dob << ", " << s.sex << ", " << s.gender  << ", " << s.address << ", " << s.score<< ", " << s.school<< ", " << s.team<< ", " << s.status << "]";
        return os;
    }
};

//========================================================================================================================================================================
//
//                                                                    学生索引结构体(Student)
//
//========================================================================================================================================================================
struct StudentIndex {
    int id;// 学号
    int row;// 行号

    bool operator<(const StudentIndex& other) const { return id < other.id; }
    bool operator>(const StudentIndex& other) const { return id > other.id; }
    bool operator==(const StudentIndex& other) const { return id == other.id; }
    bool operator!=(const StudentIndex& other) const { return id != other.id; }

    friend std::ostream& operator<<(std::ostream& os, const StudentIndex& i) {
        os << "[" << i.id << ", " << i.row<< "]";
        return os;
    }
};

//========================================================================================================================================================================
//                                                                    迷宫坐标结构体(Pos)
//========================================================================================================================================================================
struct Pos{
    int x;     //x坐标
    int y;    //y坐标
    int step; //步数
};

//========================================================================================================================================================================
//                                                                    打印任务结构体(PrintTask)
//========================================================================================================================================================================
struct PrintTask{
    int taskId;     // 任务ID
    char content[50]; // 打印内容
};

#endif // ENTITYS_H_INCLUDED
        

ArrayQueue.h 头文件


#ifndef ARRAYQUEUE_H_INCLUDED
#define ARRAYQUEUE_H_INCLUDED

#include "Entitys.h"

//************************************************************************************************************************************************************************
//
//      类名:数组队列(ArrayQueue)
//
//      概述:一个可复用的数组队列,存储结构采用数组实现,支持任意类型数据(模板泛型)。
//
//      说明:默认队列的最大容量为100,入队出队时间复杂度为O(1)
//
//************************************************************************************************************************************************************************

#define MAXSIZE 100  // 队列的最大容量


//========================================================================================================================================================================
//
//                                                                 数组队列结构体(ArrayQueue)
//
//========================================================================================================================================================================
template <typename T>
struct ArrayQueue{
    // ------------------------------------------------------------------------------------------------------
    //                                          私有成员
    // 注:私有成员只能在该结构体内部访问调用,外部通过该结构体定义(实例化)的变量(对象)不能访问调用)
    //------------------------------------------------------------------------------------------------------
    private:

        //---------------声明私有成员变量---------------

        T data[MAXSIZE];   // 存储数据,可存储字符或整数
        int head;        // 队列头,初始为-1表示空栈
        int tail;        // 队列尾,初始为-1表示空栈
        int qty;         // 用于跟踪队列中的元素数量

        //---------------声明私有成员函数---------------

    // ------------------------------------------------------------------------------------------------------
    //                                          公共成员
    // 注:公共成员能在该结构体内部访问调用,外部通过该结构体定义(实例化)的变量(对象)也能访问调用)
    //------------------------------------------------------------------------------------------------------
    public:

        //---------------声明公共成员函数---------------

        // 构造函数:外部通过该结构体定义(实例化)变量(对象)时,自动执行该函数(主要用于初始化成员变量的值)
        ArrayQueue() {
            head = 0; tail = 0; qty = 0;
            for(int i=0;i < MAXSIZE;i++) { data[i] = T(); }
        }

        int isEmpty();// 判断队列是否为空
        int isFull();// 判断队列是否已满
        void enQueue(const T& value);// 入队操作(在队列尾入队)
        T deQueue();// 出队操作(返回队列头元素,队列空返回-1)
        T getHead();// 获取队列头元素(不弹出,队列空返回-1)
        T getTail();// 获取队列尾元素(不弹出,队列空返回-1)
        int getSize();// 获取队列大小
        void print();// 打印队列数据
        void destroyQueue();// 销毁整个队列(防止内存泄漏)

        // 析构函数:自动销毁队列,避免内存泄漏
        ~ArrayQueue() {
        }
};

#endif // ARRAYQUEUE_H_INCLUDED
                
                

ArrayQueue.cpp 程序代码


#include <iostream>
#include"ArrayQueue.h"

//**********************实现公有成员函数**********************

// 判断队列是否为空
template <typename T>
int ArrayQueue<T>::isEmpty()
{
    return qty == 0;
}

// 判断队列是否已满
template <typename T>
int ArrayQueue<T>::isFull()
{
    return qty == MAXSIZE;
}

// 入队操作(在队列尾入队)
template <typename T>
void ArrayQueue<T>::enQueue(const T& value)
{
    if (isFull())
    {
        printf("队列已满!无法入队。\n");
        return;
    }

    data[tail] = value;
    tail = (tail + 1) % MAXSIZE; //实现循环队列
    qty++;
}

// 出队操作(返回队列头元素)
template <typename T>
T ArrayQueue<T>::deQueue()
{
    if(isEmpty())
    {
        printf("队列为空,无法出队。\n");
        return T();
    }

    T value =data[head];
    data[head] = T(); // 可选:清空出队的位置
    head = (head + 1) % MAXSIZE;
    qty--;
    return value;
}

// 获取队列头元素(不弹出)
template <typename T>
T ArrayQueue<T>::getHead()
{
    if (isEmpty()) {
        return T();
    }
    return data[head];
}

// 获取队列尾元素(不弹出)
template <typename T>
T ArrayQueue<T>::getTail()
{
    if (isEmpty()) {
        return T();
    }
    int index = (tail - 1 + MAXSIZE) % MAXSIZE;// 计算队尾元素的真实下标(适配循环队列)
    return data[index];
}

// 获取队列大小
template template <typename T>
int ArrayQueue<T>::getSize()
{
    return qty;
}

// 打印队列数据(仅遍历有效元素,不修改任何指针)
template <typename T>
void ArrayQueue<T>::print()
{
    if (isEmpty()) {
        printf("队列内容:空\n");
        return;
    }

    printf("队列内容:");
    // 遍历队列中实际存在的 qty 个元素
    for (int i = 0; i < qty; i++) {
        // 计算第i个有效元素的索引(适配循环队列)
        int index = (head + i) % MAXSIZE;
        printf("%d ", data[index]);
    }
    printf("\n");
    // 可选:打印队列状态(便于调试)
    printf("队列状态:头指针=%d,尾指针=%d,元素数量=%d\n", head, tail, qty);
}         

// 显式实例化常用类型(避免链接错误,可选)
template class ArrayQueue<int>;
template class ArrayQueue<char>;
template class ArrayQueue<float>;
template class ArrayQueue<std::string>;
template class ArrayQueue<Student>; // 新增:显式实例化Student类型
template class ArrayQueue<Pos>; // 新增:显式实例化Pos类型
template class ArrayQueue<PrintTask>; // 新增:显式实例化PrintTask类型

main.cpp 测试代码

#include "ArrayQueue.h"
#include <iostream>
#include <string>

int main() {
    ArrayQueue<int> queues;

    // 1. 入队测试
    printf("\n=== 入队测试 ===\n");
    queues.enQueue(1);
    queues.enQueue(2);
    queues.enQueue(3);
    queues.enQueue(4);
    queues.enQueue(5);
    printf("入队1、2、3、4、5后:\n");
    queues.print();

    // 2. 出队测试
    printf("\n=== 出队测试 ===\n");
    printf("出队3次:");
    printf("%d ",queues.deQueue());
    printf("%d ",queues.deQueue());
    printf("%d \n",queues.deQueue());
    printf("出队后");
    queues.print();
    
    return 0;
}

输出结果


=== 入队测试 ===
入队1、2、3、4、5后:
队列内容:1 2 3 4 5
队列状态:头指针=0,尾指针=5,元素数量=5

=== 出队测试 ===
出队3次:1 2 3
出队后队列内容:4 5
队列状态:头指针=3,尾指针=5,元素数量=2
    

六、注意事项

模板类型约束

  • 模板类型需支持</>/==运算符(内置类型如int/string默认支持,自定义类型需重载);
  • 自定义类型需重载<<运算符(打印输出时使用)。

显式实例化:代码末尾的template class LinkedQueue<int>;等显式实例化,避免链接错误,新增类型需补充显式实例化。

七、应用场景

八、总结

本教程通过代码解析和实战示例,覆盖了队列的核心原理、关键操作实现和实际使用场景。队列作为基础数据结构,在缓存机制、任务调度、消息队列等场景中有着广泛应用,掌握数组队列的实现,是深入学习更复杂数据结构(如优先级队列)的重要基础。


返回顶部